#pragma once

#include <MCF/Code/StdMCF.hpp>
#include <MCF/Code/WindowClass.hpp>

class VGAClass sealed : public MCF::WindowClass {
private:
	static const std::size_t _CHAR_WIDTH		= 16;
	static const std::size_t _CHAR_HEIGHT		= 16;
	static const std::size_t _COLUMN_COUNT		= 40;
	static const std::size_t _ROW_COUNT			= 30;
	static const std::size_t _VRAM_WORD_COUNT	= 2048;

	static const COLORREF _COLOR_TABLE[];
public:
	static const std::size_t SCREEN_WIDTH		= _CHAR_WIDTH * _COLUMN_COUNT;
	static const std::size_t SCREEN_HEIGHT		= _CHAR_HEIGHT * _ROW_COUNT;
private:
	typedef struct _tagCharInfo {
		BYTE byBkgColor;
		BYTE byColor;
		BYTE byCharCode;
	} _CHARINFO;

	class _VGAThreadClass;
private:
	static bool _EventVGASetFontProc(std::intptr_t nContext, std::intptr_t nArg1, std::intptr_t nArg2, std::intptr_t nArg3);
	static bool _EventVGASetFontWordProc(std::intptr_t nContext, std::intptr_t nArg1, std::intptr_t nArg2, std::intptr_t nArg3);
	static bool _EventVGASetCharProc(std::intptr_t nContext, std::intptr_t nArg1, std::intptr_t nArg2, std::intptr_t nArg3);
	static bool _EventVGASetOffsetProc(std::intptr_t nContext, std::intptr_t nArg1, std::intptr_t nArg2, std::intptr_t nArg3);
	static bool _EventVGAGetOffsetProc(std::intptr_t nContext, std::intptr_t nArg1, std::intptr_t nArg2, std::intptr_t nArg3);
private:
	_VGAThreadClass *_m_lpVGAThread;
	DWORD _m_dwLastUpdatedTime;
	volatile bool _m_bTerminateNow;

	MCF::CriticalSection _m_csLock;
	WORD _m_arwFontRAM[256][_CHAR_WIDTH * _CHAR_HEIGHT / (sizeof(WORD) * CHAR_BIT)];
	_CHARINFO _m_arVRAM[_VRAM_WORD_COUNT];
	bool _m_arbVRAMDirtyFlags[_VRAM_WORD_COUNT];
	std::size_t _m_uOffset;
public:
	VGAClass();
	virtual ~VGAClass();
protected:
	virtual void _PostSubclass() override;
	virtual void _PreUnsubclass() override;

	virtual bool _OnCreate(LPCREATESTRUCT lpCreateStruct) override;
	virtual void _OnDestroy() override;
	virtual bool _OnEraseBkgnd(HDC hDC) override;
	virtual void _OnPaint() override;
private:
	void _SetFont(BYTE byIndex, const WORD *lpwFontBuffer);
	void _SetFontWord(BYTE byIndex, std::size_t uIndexInFont, WORD wVal);
	void _SetChar(std::size_t uIndex, BYTE byCharCode, BYTE byColor, BYTE byBkgColor);
	void _SetOffset(std::size_t uNewOffset);
	std::size_t _GetOffset() const;
};
